#include "GravityGame.h"
#include <Windows.h>
#include <CommCtrl.h>
#include "resource.h"
#include "SuperString.h"

INT_PTR CALLBACK D_Proc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );

GravityGame::GravityGame( HINSTANCE * hinst, int hncmd ) : m_hinst ( hinst )
{
	if ( !m_current_instance )
	{
		m_current_instance = this;
		m_pos.x = 0;
		m_pos.y = 0;
		m_current_instance->m_bc = nullptr;

		WNDCLASSEX	wndclass;

		wndclass.cbSize			= sizeof(wndclass);
		wndclass.style			= CS_HREDRAW | CS_VREDRAW;
		wndclass.lpfnWndProc	= GravityGame::WndProc;
		wndclass.cbClsExtra		= 0;
		wndclass.cbWndExtra		= 0;
		wndclass.hInstance		= *m_hinst;
		wndclass.hIcon			= LoadIcon (NULL, IDI_APPLICATION);
		wndclass.hCursor		= LoadCursor (NULL, IDC_ARROW);
		wndclass.hbrBackground	= WHITE_BRUSH;
		wndclass.lpszMenuName	= NULL;
		wndclass.lpszClassName	= "Gravity Game!";
		wndclass.hIconSm		= LoadIcon (NULL, IDI_APPLICATION);

		RegisterClassEx (&wndclass);

		m_hwnd = CreateWindow ("Gravity Game!",	// window class name
				"Lab 3 - Gravity fun!",				// window caption
				WS_OVERLAPPEDWINDOW,				// window style
				CW_USEDEFAULT,						// initial x position
				CW_USEDEFAULT,						// initial y position
				1200,								// initial x size
				800,								// initial y size
				NULL,								// parent window handle
				NULL,								// window menu handle
				*m_hinst,							// program instance handle
				NULL);								// creation parameters

		DWORD temp = GetLastError();

		ShowWindow (m_hwnd, hncmd);

		UpdateWindow (m_hwnd);
	}
}

void GravityGame::StartGame()
{
	m_timer = SetTimer( m_hwnd,	// window handle
			0,					// id of the timer message, leave 0 in this case
			16,					// millis
			Timer );			// Callback
}

void GravityGame::QuitGame()
{
	KillTimer( m_hwnd, m_timer );
}

HWND * GravityGame::GetHWND()
{
	return &m_hwnd;
}

GravityGame::~GravityGame()
{

}


GravityGame * GravityGame::m_current_instance = nullptr;

LRESULT CALLBACK GravityGame::WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
	HDC			hdc, hbdc;
	PAINTSTRUCT ps;
	RECT		rect;
	POINT		window;
	POINT		mouse;
	BlipController * bc = m_current_instance->m_bc;

	static int	blips(5000);
	static bool	drawtoscreen = false;
	static bool control_well = false;
	static UINT oldstyle = 0;
	static HWND dlgbox;
	static HINSTANCE hinst;

	switch (message)
	{
	case WM_CREATE:
		hinst = (HINSTANCE)GetWindowLong( hwnd, GWLP_HINSTANCE );
		return 0;

	case WM_PAINT:
		GetClientRect( hwnd, &rect );

		hbdc = CreateCompatibleDC( NULL );
		SelectObject( hbdc, *(bc->Tick()) );
		hdc = BeginPaint( hwnd, &ps );

		BitBlt( hdc,  0, 0, rect.right, rect.bottom,
				hbdc, 0, 0, SRCCOPY );

		EndPaint( hwnd, &ps );

		DeleteObject(hbdc);
		return 0;

	case WM_SIZE:
		if ( !m_current_instance->m_bc )
		{
			GetClientRect( hwnd, &rect );
			bc = m_current_instance->m_bc = new BlipController( blips, rect.right, rect.bottom, 16, 2.0, 3.0, &hwnd );
		}
		GetWindowRect( hwnd, &rect );
		window.x = rect.left + 5;
		window.y = rect.top + 25;
		bc->SetCTSOffset( window );
		GetClientRect( hwnd, &rect );
		bc->SetSize( rect.right, rect.bottom );
		return 0;

	case WM_MOVE:
		GetWindowRect( hwnd, &rect );
		window.x = rect.left + 5;
		window.y = rect.top + 25;
		bc->SetCTSOffset( window );
		return 0;

	case WM_LBUTTONDOWN:
		bc->SetPull(true);
		return 0;

	case WM_LBUTTONUP:
		bc->SetPull(false);
		return 0;

	case WM_RBUTTONDOWN:
		bc->Reset();
		return 0;

	case WM_RBUTTONUP:
		
		return 0;

	case WM_QUIT:
		delete bc;
		m_current_instance->m_bc = nullptr;
		break;

	case WM_DESTROY:
		PostQuitMessage (0);
		return 0;

	case WM_MOUSEMOVE:
		if ( !control_well )
		{
			GetCursorPos( &mouse );
			bc->UpdateWell( mouse );
		}
		return 0;

	case WM_KEYDOWN:
		switch ( wParam )
		{
		case VK_F1:
			DialogBoxParam( hinst, MAKEINTRESOURCE( IDD_DIALOG1 ), hwnd, D_Proc, (LPARAM)bc );
			break;
		case VK_F2:
			control_well = !control_well;
			if ( control_well )
				SetTimer( hwnd, m_current_instance->m_timer_control, 10, TimerControl );
			else if ( !control_well )
				KillTimer( hwnd, m_current_instance->m_timer_control );
			break;
		case VK_F11:
            if ( !oldstyle )
                    oldstyle = GetWindowLong( hwnd, GWL_STYLE );
            drawtoscreen = !drawtoscreen;
            if ( drawtoscreen )
            {
                    UINT    style = GetWindowLong( hwnd, GWL_STYLE );
                    POINT temp;
                    temp.x = GetSystemMetrics( SM_CXSCREEN );
                    temp.y = GetSystemMetrics( SM_CYSCREEN );

                    style &= ~( WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZE | WS_THICKFRAME );
                    SetWindowLong( hwnd, GWL_STYLE, style );
                    SetWindowPos( hwnd, NULL, 0, 0, temp.x, temp.y, NULL );
                    bc->SetSize( temp.x, temp.y );
            }
            else
            {
                    SetWindowLong( hwnd, GWL_STYLE, oldstyle );
                    SetWindowPos( hwnd, NULL, 100, 100, 1200, 800, NULL );
            }
            break;
		}
		
		return 0;
	}

	return DefWindowProc (hwnd, message, wParam, lParam);
}

//Timer function that sends a message to the window (Wanted to demonstrate a seperate callback function)
VOID CALLBACK GravityGame::Timer( HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime ) {
	RECT	rect;
	GetClientRect( hwnd, &rect) ;
	InvalidateRect( hwnd, &rect, false );
}

VOID CALLBACK GravityGame::TimerControl( HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime ) {
	static int x = 1;
	static int y = -1;
	RECT client;
	GetClientRect( hwnd, &client );

	m_current_instance->m_pos.x += x;
	m_current_instance->m_pos.y += y;

	if ( m_current_instance->m_pos.x > client.right )
		x = -1;
	else if ( m_current_instance->m_pos.x < 0 )
		x = 1;

	if ( m_current_instance->m_pos.y > client.bottom )
		y = -1;
	else if ( m_current_instance->m_pos.y < 0 )
		y = 1;

	m_current_instance->m_bc->UpdateWell( m_current_instance->m_pos );
	m_current_instance->m_bc->SetPull( true );
}

INT_PTR CALLBACK D_Proc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	static BlipController * bc;
	SuperString ss;
	int num_blips;
	static HWND tbfield;
	static HWND tbgrav;

	switch ( msg )
	{
	case WM_INITDIALOG:
		bc = (BlipController *)lParam;
		ss.setString( bc->GetNumBlips() );
		tbfield = GetDlgItem( hwnd, IDC_SLIDER1 );
		tbgrav = GetDlgItem( hwnd, IDC_SLIDER2 );
		SendDlgItemMessage( hwnd, IDC_CHECK1, BM_SETCHECK, bc->GetClipping(), NULL );
		SendDlgItemMessage( hwnd, IDC_CHECK2, BM_SETCHECK, bc->GetColor(), NULL );
		SetDlgItemText( hwnd, IDC_EDIT1, ss.getString() );
		SendMessage( tbfield, TBM_SETRANGEMIN, true, 0 );
		SendMessage( tbfield, TBM_SETRANGEMAX, true, 10 );
		SendMessage( tbfield, TBM_SETPOS, true, bc->GetField() );

		SendMessage( tbgrav, TBM_SETRANGEMIN, true, -10 );
		SendMessage( tbgrav, TBM_SETRANGEMAX, true, 10 );
		SendMessage( tbgrav, TBM_SETPOS, true, bc->GetGrav() );

		return 0;

	case WM_COMMAND:
		switch ( wParam )
		{
		case IDOK:
			num_blips = GetDlgItemInt( hwnd, IDC_EDIT1, NULL, false );
			if ( num_blips != bc->GetNumBlips() )
				bc->SetNumBlips( num_blips );
			bc->SetField( SendMessage( tbfield, TBM_GETPOS, NULL, NULL ) );
			bc->SetGravity( SendMessage( tbgrav, TBM_GETPOS, NULL, NULL ) );
			if ( SendDlgItemMessage( hwnd, IDC_CHECK1, BM_GETCHECK, NULL, NULL ) == BST_CHECKED )
				bc->SetClipping( true );
			else
				bc->SetClipping( false );
			if ( SendDlgItemMessage( hwnd, IDC_CHECK2, BM_GETCHECK, NULL, NULL ) == BST_CHECKED )
				bc->SetStaticColor( true );
			else
				bc->SetStaticColor( false );
		case IDCANCEL:
			EndDialog( hwnd, 0 );
		}
	}
	return 0;
}